/*
 * The MIT License
 *
 * Copyright 2012 http://generic-toolset.googlecode.com.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package org.rlpw.swing;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.ListCellRenderer;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.event.EventListenerList;

/**
 *
 * @param <T> the type of the elements
 */
public class JOCheckList<T> extends JOList<T> implements Checkable<T> {

    private CheckModel<T> checked;
    private EventListenerList listeners = new EventListenerList();
    private CheckListRenderer checkRenderer;

    public JOCheckList() {
        this(new DefaultModel<T>());
    }

    public JOCheckList(Model<T> dm) {
        super(dm);
        checked = new DefaultCheckModel(this);
        checkRenderer = new CheckListRenderer();
        super.setCellRenderer(checkRenderer);
    }

    /** {@inheritDoc} */
    @Override
    public CheckModel<T> getCheckModel() {
        return checked;
    }

    /** {@inheritDoc} */
    @Override
    public void setCellRenderer(ListCellRenderer cellRenderer) {
        if (checkRenderer != null) {
            checkRenderer.setRenderer(cellRenderer);
        } else {
            super.setCellRenderer(cellRenderer);
        }
    }

    /**
     * Adds an {@code ActionListener} to the {@code JOCheckList}.
     * 
     * @param l the {@code ActionListener} to be added
     */
    public void addActionListener(ActionListener l) {
        listeners.add(ActionListener.class, l);
    }

    /**
     * Removes an {@code ActionListener} from the {@code JOCheckList}.
     * 
     * @param l the {@code ActionListener} to be removed
     */
    public void removeActionListener(ActionListener l) {
        listeners.remove(ActionListener.class, l);
    }

    /**
     * Notifies all listeners that have registered interest for notification on this event type.<br/>
     * Updated all added views on the current {@code Model}. 
     * 
     * @see  org.rlpw.swing.Model#updateViews
     */
    protected synchronized void fireActionEvent() {
        getDataModel().updateViews();
        ActionEvent event = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "");
        for (ActionListener l : listeners.getListeners(ActionListener.class)) {
            l.actionPerformed(event);
        }
    }

    private class CheckListRenderer implements ListCellRenderer {

        private ListCellRenderer renderer;

        public CheckListRenderer() {

            renderer = new DefaultListCellRenderer() {

                @Override
                public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
                    JLabel label = new JLabel(value.toString());
                    label.setEnabled(list.isEnabled());
                    adapt(label, super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus));
                    return label;
                }
            };

            addMouseListener(new MouseAdapter() {

                @Override
                public void mouseClicked(MouseEvent e) {

                    if (e.getX() > 18) {
                        return;
                    }
                    int index = locationToIndex(e.getPoint());
                    getCheckModel().setChecked(index, !getCheckModel().isChecked(index));
                    Rectangle rect = getCellBounds(index, index);
                    repaint(rect);

                    fireActionEvent();
                }
            });
        }

        @Override
        public Component getListCellRendererComponent(final JList list, Object value,
                final int index, boolean isSelected, boolean hasFocus) {
            JCheckBox box = new JCheckBox();
            box.setEnabled(list.isEnabled());

            box.setSelected(getCheckModel().isChecked((T) value));

            JPanel panel = new JPanel(new BorderLayout());
            panel.add(box, BorderLayout.WEST);
            Component listCellRendererComponent = renderer.getListCellRendererComponent(list, value, index, isSelected, hasFocus);
            panel.add(listCellRendererComponent, BorderLayout.CENTER);

            adapt(box, listCellRendererComponent);
            adapt(panel, listCellRendererComponent);

            Border border = null;
            if (hasFocus) {
                if (isSelected) {
                    border = UIManager.getBorder("List.focusSelectedCellHighlightBorder");
                }
                if (border == null) {
                    border = UIManager.getBorder("List.focusCellHighlightBorder");
                }
            } else {
                border = UIManager.getBorder("List.cellNoFocusBorder");
            }
            panel.setBorder(border);

            return panel;
        }

        private void adapt(JComponent comp, Component source) {
       
            comp.setOpaque(true);
            comp.setForeground(source.getForeground());
            comp.setBackground(source.getBackground());
        }

        ListCellRenderer getRenderer() {
            return renderer;
        }

        void setRenderer(ListCellRenderer renderer) {
            this.renderer = renderer;
        }
    }
}
